from random import random

# ===========================================================================
# Brilliant: Hunger Games
# Ivan Wang
# ---------------------------------------------------------------------------
# Modeled after Tit for Tat, the algorithm assesses each player's
# reputation, R, as their probability of hunting.
#
# For the first few rounds, the player attempts to do its share of
# hunting for the threshold m while simultaneously attempting to stay
# within the bounds of a favorable reputation.

# After a couple of rounds, the algorithm seeks to copy the other
# players' behavior based on their reputation, R. For each player,
# it will hunt with probability R and slack with probability 1 - R.
# ===========================================================================

class Player:
    MAX_REP = 0.8 # Maximum reputation to strive for
    MIN_REP = 0.4  # Minimum reputation to strive for
    START_ROUNDS = 5 # Number of rounds to build a reputation
    total_hunts = 0 # Keep track of total hunts for reputation
    total_slacks = 0 # Keep track of total slacks for reputation
    
    def __init__(self):
        pass
    
    # =================================================================================
    # Given the environmental factors of the current round, return a list of decisions
    # (hunt or slack) corresponding to each player's reputation.
    # ---------------------------------------------------------------------------------
    # round_number: integer, the number round you are in.
    # current_food: integer, the amount of food you have.
    # current_reputation: float, your current reputation.
    # m: integer, the threshold cooperation value for this round.
    # player_reputations: list of floats, the reputations of the remaining players.
    # =================================================================================
    def hunt_choices(self, round_number, current_food, current_reputation, m, player_reputations):
        p = len(player_reputations) # number of players
        threshold = int(float(m) / p) # calculate average hunts/player for threshold
        
        # For first few rounds, focus on gaining reputation, trying to meet threshold m
        if round_number <= self.START_ROUNDS:
            hunts = self.boundToReputation(threshold, p - threshold)
            self.total_hunts += hunts
            self.total_slacks += p - hunts
            
            # Since players are randomly distributed, hunt locations don't matter
            return ["h"] * hunts + ["s"] * (p - hunts)
        
        # Otherwise, if past first round...
        hunt_decisions = []
        hunts = 0 # count hunts this round
        checks = 0 # number of decisions made
        
        while not hunt_decisions:
            # For each player, hunt with probability rep, slack with probability 1 - rep
            for rep in player_reputations:
                if random() < rep:
                    hunt_decisions.append("h")
                    hunts += 1
                else:
                    hunt_decisions.append("s")

            # If hunts do not maintain reputation, recalculate
            if not self.checkReputation(hunts, p - hunts) and checks < 100:
                hunt_decisions = []
                hunts = 0
            
            # Break out of the loop if too many checks
            checks += 1
        
        self.total_hunts += hunts
        self.total_slacks += p - hunts
        return hunt_decisions

    # =================================================================================
    # Called after each round with a list of food earnings made that round.
    # ---------------------------------------------------------------------------------
    # food_earnings: list of integers, corresponding to food gained per decision.
    # =================================================================================
    def hunt_outcomes(self, food_earnings):
        pass

    # =================================================================================
    # Called after each round with the award and number of cooperators.
    # ---------------------------------------------------------------------------------
    # award: integer, total food bonus received from cooperating
    # number_cooperators: integer, how many players cooperated over the last round.
    # =================================================================================
    def round_end(self, award, m, number_cooperators):
        pass

    # =================================================================================
    # Check if number of hunts will maintain good reputation
    # ---------------------------------------------------------------------------------
    # hunts: number of hunts this round
    # slacks: number of slacks this round
    # =================================================================================
    def checkReputation(self, hunts, slacks):
        totalHunts = float(self.total_hunts + hunts)
        newReputation = totalHunts / (totalHunts + self.total_slacks + slacks)
        return newReputation >= self.MIN_REP and newReputation <= self.MAX_REP

    # =================================================================================
    # Returns number of hunts ensuring good reputation is maintained
    # ---------------------------------------------------------------------------------
    # hunts: number of hunts this round
    # slacks: number of slacks this round
    # =================================================================================
    def boundToReputation(self, hunts, slacks):
        if self.checkReputation(hunts, slacks):
            return hunts

        # If reputation not good, add or subtract hunts to offset
        total = hunts + slacks
        totalHunts = float(self.total_hunts + hunts)
        newReputation = totalHunts / (totalHunts + self.total_slacks + slacks)
        if newReputation < self.MIN_REP:
            return min(total, hunts + total / 5)
        else:
            return max(0, hunts - total / 5)
        
